<?php
class SnowFlake
{
    const TWEPOCH = 1288834974657; // 开始时间截，一般取系统的最近时间（一旦确定不能变动）
 
    const WORKER_ID_BITS     = 5; // 机器标识id位数
    const DATACENTER_ID_BITS = 5; // 数据标识id位数
    const SEQUENCE_BITS      = 12; // 序列在id中占的位数

    private $maxWorkerId     = -1 ^ (-1 << self::WORKER_ID_BITS); // 支持的最大机器id，结果是31
    private $maxDatacenterId = -1 ^ (-1 << self::DATACENTER_ID_BITS); // 支持的最大数据标识id，结果是31

    private $workerIdShift      = self::SEQUENCE_BITS; // 机器id左移12位
    private $datacenterIdShift  = self::SEQUENCE_BITS + self::WORKER_ID_BITS; // 数据标识id左移17位
    private $timestampLeftShift = self::SEQUENCE_BITS + self::WORKER_ID_BITS + self::DATACENTER_ID_BITS; // 时间毫秒左移22位
    private $sequenceMask       = -1 ^ (-1 << self::SEQUENCE_BITS); // 生成序列的掩码，这里为4095
    private $workerId; // 工作机器ID(0~31)
    private $datacenterId; // 数据中心ID(0~31)
    private $sequence; // 毫秒内序列(0~4095)
 
    private $lastTimestamp = -1; // 上次生成id时间戳
 
    public function __construct($workerId, $datacenterId, $sequence = 0)
    {
        if ($workerId > $this->maxWorkerId || $workerId < 0) {
            throw new Exception("worker Id can't be greater than {$this->maxWorkerId} or less than 0");
        }
 
        if ($datacenterId > $this->maxDatacenterId || $datacenterId < 0) {
            throw new Exception("datacenter Id can't be greater than {$this->maxDatacenterId} or less than 0");
        }
 
        $this->workerId     = $workerId;
        $this->datacenterId = $datacenterId;
        $this->sequence     = $sequence;
    }
    
    /**
     * 获得下一个ID
     */
    public function nextId()
    {
        $timestamp = $this->timeGen();
        
        // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退
        if ($timestamp < $this->lastTimestamp) {
            $diffTimestamp = bcsub($this->lastTimestamp, $timestamp);
            throw new Exception("Clock moved backwards.  Refusing to generate id for {$diffTimestamp} milliseconds");
        }
        
        // 如果是同一时间生成的，则进行毫秒内序列
        if ($this->lastTimestamp == $timestamp) {
            $this->sequence = ($this->sequence + 1) & $this->sequenceMask;
            // 毫秒内序列溢出
            if (0 == $this->sequence) {
                // 阻塞到下一个毫秒,获得新的时间戳
                $timestamp = $this->tillNextMillis($this->lastTimestamp);
            }
        } else {
            // 时间戳改变，毫秒内序列重置
            $this->sequence = 0;
        }
 
        $this->lastTimestamp = $timestamp;
        
        // 移位并通过或运算拼到一起组成64位的ID
        return (($timestamp - self::TWEPOCH) << $this->timestampLeftShift) |
        ($this->datacenterId << $this->datacenterIdShift) |
        ($this->workerId << $this->workerIdShift) |
        $this->sequence;
    }
    
    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     * @param lastTimestamp
     */
    protected function tillNextMillis($lastTimestamp)
    {
        $timestamp = $this->timeGen();
        while ($timestamp <= $lastTimestamp) {
            $timestamp = $this->timeGen();
        }
 
        return $timestamp;
    }
    
    // 返回当前毫秒时间，13位
    protected function timeGen()
    {
        return floor(microtime(true) * 1000);
    }
}

// 分布式系统内需要区分数据中心id或机器id，否则会产生id碰撞
$snowflake1 = new SnowFlake(1, 2);
$snowflake2 = new SnowFlake(2, 2);
$chan = new chan(100000);
$n = 100000;
 
for ($i = 0; $i < $n / 2; $i++) {
    go(function () use ($snowflake1, $chan) {
        $id = $snowflake1->nextId();
        $chan->push($id);
    });
}
for ($i = 0; $i < $n / 2; $i++) {
    go(function () use ($snowflake2, $chan) {
        $id = $snowflake2->nextId();
        $chan->push($id);
    });
}
 
go(function () use ($chan, $n) {
    $arr = [];
    for ($i = 0; $i < $n; $i++) {
        $id = $chan->pop();
        if(isset($arr[$id])){
            echo "ID->{$id}已存在" . PHP_EOL;
        }
        $arr[$id] = 1;
    }
});
 
$chan->close();
echo "ok";